package comm_test;



import java.io.IOException;
import java.net.UnknownHostException;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;




import comm.ClientInterface;
import comm.ClientMessage;
import comm.ClientSideMessage;
import comm.MoodType;
import comm.ServerInterface;

import comm.ServerSideMessage;
import comm.ClientSideMessage.Type;

/**
 * 
 * This class puts to the test the following:
 * 
 * - Tests the connection of clients with server
 * - Tests if the clients can exchange messages with server
 * 
 * - puts under the test the methods of 
 * each class ClientInterface and ServerInterface.
 * 
 */

public class NetworkCommunicationTest {
	public static final String HOST = "localhost";
	public static final String CLIENT_1_NICKNAME = "monkey";
	public static final String CLIENT_2_NICKNAME = "popeye";

	private ServerInterface serverInterface;
	private ClientInterface clientInterface1;
	private ClientInterface clientInterface2;


	@Before
	public void prepare(){
		serverInterface = new ServerInterface();
		Assert.assertNotNull(serverInterface);
		clientInterface1 = new ClientInterface();
		Assert.assertNotNull(clientInterface1);
		clientInterface2 = new ClientInterface();
		Assert.assertNotNull(clientInterface2);
	}

	@After
	public void cleanUp(){
		serverInterface = null;
		clientInterface1 = null;
		clientInterface2 = null;
	}
	/**
	 * This test is used to test the following:
	 * 
	 * Send a Moodish message from client to the server which relay the message to another client.
	 * 
	 * methods under test:
	 * 
	 *		@class	ServerInterface
	 *			@method void sendMoodishMessage(String fromNickename, String toNickname, String moodishMessage)
	 *
	 *		@class	ClientInterface
	 *			@method void sendMoodishMessage(String moodishMessage)
	 *
	 * @throws UnknownHostException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	@Test
	public void test_sendMoodishMessage() throws UnknownHostException, IOException, InterruptedException{
		MoodType moodishMessageExpected = MoodType.Cheerful;
		ClientSideMessage.Type moodishMessageTypeExpected = ClientSideMessage.Type.MOODISH_MESSAGE;

		System.out.println("\n\n*** BEGIN TEST *** ");
		System.out.println("**@class=NetworkCommunicationTest @method=sendMoodishMessage()**\n");

		//Preparing Test.. Giving Time for resources to be free
		Thread.sleep(100);

		//open connection
		serverInterface.start();

		clientInterface1.connect(HOST, CLIENT_1_NICKNAME);
		clientInterface2.connect(HOST, CLIENT_2_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//client send moodish message
		clientInterface1.sendMoodishMessage(moodishMessageExpected.toString());

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		/**
		 *  Verifies at Server if 
		 *  { sender, content moodish message and type of message are expected }
		 */
		ServerSideMessage msgAtServer = null;
		if(serverInterface.hasNextMessage()){
			msgAtServer = serverInterface.getNextMessage();

			Assert.assertEquals("moodish message type should be " + moodishMessageTypeExpected.toString(),moodishMessageTypeExpected.toString(),msgAtServer.getType().toString());
			Assert.assertEquals("moodish message content should be " + moodishMessageExpected.toString(),moodishMessageExpected.toString(), msgAtServer.getPayload().toString());
			Assert.assertEquals("nickname of the sender should be " + CLIENT_1_NICKNAME,CLIENT_1_NICKNAME, msgAtServer.getClientNickname());	
		}

		/**
		 *  Simulates relay of the message
		 *  Msg sent by client A will reach client B by using the server
		 */

		if(msgAtServer != null){
			serverInterface.sendMoodishMessage(msgAtServer.getClientNickname(), CLIENT_2_NICKNAME, msgAtServer.getPayload());
		}

		Thread.sleep(10);
		/**
		 *  Verifies { sender, content moodish message and type of message are expected }
		 */
		ClientSideMessage msgAtClient = null;
		if(clientInterface2.hasNextMessage()){
			msgAtClient = clientInterface2.getNextMessage();

			Assert.assertEquals("moodish message type should be " + moodishMessageTypeExpected.toString(),moodishMessageTypeExpected.toString(),msgAtClient.getType().toString());
			Assert.assertEquals("moodish message content should be " + moodishMessageExpected.toString(),moodishMessageExpected.toString(), msgAtClient.getPayload().toString());
			Assert.assertEquals("nickname of the sender should be " + CLIENT_1_NICKNAME,CLIENT_1_NICKNAME, msgAtClient.getSendersNickname().toString());	
		}

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//Close connection
		clientInterface1.disconnect();
		clientInterface2.disconnect();

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);
		serverInterface.shutdown();


		//Preparing Test.. Giving Time for other processes to finish
		Thread.sleep(100);
		System.out.println("\n**@class=NetworkCommunicationTest @method=sendMoodishMessage()**");
		System.out.println("*** END TEST ***\n");
	}

	/**
	 * Tests the connection of a given client with the server
	 * 
	 * Status of connection = [ Connected or Disconnected ]
	 * 
	 * 
	 * 	 * methods under test:
	 * 
	 *		@class	ClientInterface
	 *			@method void connect()
	 *			@method void disconnect()
	 *
	 *		@class	ServerInterface
	 *			@method void disconnectClient(String disconnector)
	 *
	 *
	 * @throws InterruptedException 
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	@Test
	public void test_clientConnectionStatus() throws InterruptedException, UnknownHostException, IOException{
		System.out.println("\n\n*** BEGIN TEST ***");
		System.out.println("**@class=NetworkCommunicationTest @method=clientConnectionStatus\n**");


		//Preparing Test.. Giving Time for resources to be free
		Thread.sleep(100);

		serverInterface.start();

		//connects client with server
		clientInterface1.connect(HOST, CLIENT_1_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//Verifies the state of connection after client connection
		Assert.assertTrue("ServerSide ---- client should be connected!", serverInterface.clientIsConnected(CLIENT_1_NICKNAME));
		Assert.assertFalse("ServerSide ---- client should be disconnected!", serverInterface.clientIsConnected(CLIENT_2_NICKNAME));
		Assert.assertTrue("ClientSide ---- client should be connected!", clientInterface1.isConnected());

		//disconnects client
		clientInterface1.disconnect();

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//Verifies state of client connection after disconnection
		Assert.assertFalse("ServerSide ---- client should be connected!", serverInterface.clientIsConnected(CLIENT_1_NICKNAME));
		Assert.assertFalse("ClientSide ---- client should be disconnected!", clientInterface1.isConnected());

		serverInterface.shutdown();

		//Preparing Test.. Giving Time for other processes to finish
		Thread.sleep(100);
		System.out.println("\n**@class=NetworkCommunicationTest @method=clientConnectionStatus**");
		System.out.println("*** END TEST ***\n");
	}

	/**
	 * Tests Request to be friend/unfriend of another client.
	 * 
	 * 
	 * 	 * methods under test:
	 * 
	 *		@class	ClientInterface
	 *			@method void friendship()
	 *			@method void unfriendship()
	 *
	 *		@class	ServerInterface
	 *			@method void sendNewFriendship(String toNickname, String newFriendship)
	 *			@method sendNewUnfriendship(String toNickname, String unfriendship)
	 *
	 *
	 * @throws InterruptedException 
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	@Test
	public void test_FrienshipRequests() throws InterruptedException, UnknownHostException, IOException{
		System.out.println("\n\n*** BEGIN TEST ***");
		System.out.println("**@class=NetworkCommunicationTest @method=FrienshipRequests()\n**");


		//Preparing Test.. Giving Time for resources to be free
		Thread.sleep(100);

		serverInterface.start();

		//connects clients with server
		clientInterface1.connect(HOST, CLIENT_1_NICKNAME);
		clientInterface2.connect(HOST, CLIENT_2_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//  --------------
		// |  FRIENDSHIP |
		//  --------------


		//client A send friendship request to client B
		clientInterface1.friendship(CLIENT_2_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		Assert.assertTrue(serverInterface.hasNextMessage());
		ServerSideMessage msgAtServer = serverInterface.getNextMessage();

		Assert.assertEquals("should be friendship request " + ServerSideMessage.Type.FRIENDSHIP.toString(),ServerSideMessage.Type.FRIENDSHIP.toString(),msgAtServer.getType().toString());
		Assert.assertEquals("should be friendship request from " + CLIENT_1_NICKNAME,CLIENT_1_NICKNAME, msgAtServer.getClientNickname().toString());
		Assert.assertEquals("should be friendship request to  " + CLIENT_2_NICKNAME,CLIENT_2_NICKNAME, msgAtServer.getPayload().toString());	

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//server relays request to client B

		serverInterface.sendNewFriendship(msgAtServer.getPayload().toString(), msgAtServer.getClientNickname().toString());

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//client B receives friendship request relayed by server
		Assert.assertTrue(clientInterface2.hasNextMessage());
		ClientSideMessage msgAtClient = clientInterface2.getNextMessage();

		Assert.assertEquals("should be friendship request " + ClientSideMessage.Type.FRIENDSHIP.toString(),ClientSideMessage.Type.FRIENDSHIP.toString(),msgAtClient.getType().toString());
		Assert.assertEquals("should be friendship request from " + CLIENT_1_NICKNAME,CLIENT_1_NICKNAME, msgAtClient.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER, msgAtClient.getSendersNickname().toString());	

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//  --------------
		// | UNFRIENDSHIP |
		//  --------------


		//client B send unfriendship request to client A
		clientInterface2.unfriendship(CLIENT_1_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		Assert.assertTrue(serverInterface.hasNextMessage());
		ServerSideMessage msgAtServer2 = serverInterface.getNextMessage();

		Assert.assertEquals("should be unfriendship request " + ServerSideMessage.Type.UNFRIENDSHIP.toString(),ServerSideMessage.Type.UNFRIENDSHIP.toString(),msgAtServer2.getType().toString());
		Assert.assertEquals("should be unfriendship request from " + CLIENT_2_NICKNAME,CLIENT_2_NICKNAME, msgAtServer2.getClientNickname().toString());
		Assert.assertEquals("should be unfriendship request to  " + CLIENT_1_NICKNAME,CLIENT_1_NICKNAME, msgAtServer2.getPayload().toString());	

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//server relays request to client A

		serverInterface.sendNewUnfriendship(msgAtServer2.getPayload().toString(), msgAtServer2.getClientNickname().toString());

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//client B receives friendship request relayed by server
		Assert.assertTrue(clientInterface1.hasNextMessage());
		ClientSideMessage msgAtClient2 = clientInterface1.getNextMessage();

		Assert.assertEquals("should be unfriendship request " + ClientSideMessage.Type.UNFRIENDSHIP.toString(),ClientSideMessage.Type.UNFRIENDSHIP.toString(),msgAtClient2.getType().toString());
		Assert.assertEquals("should be unfriendship request from " + CLIENT_2_NICKNAME,CLIENT_2_NICKNAME, msgAtClient2.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER, msgAtClient2.getSendersNickname().toString());	


		//Close connection
		clientInterface1.disconnect();
		clientInterface2.disconnect();
		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		serverInterface.shutdown();

		//Preparing Test.. Giving Time for other processes to finish
		Thread.sleep(100);
		System.out.println("\n**@class=NetworkCommunicationTest @method=FrienshipRequests()**");
		System.out.println("*** END TEST ***\n");
	}

	/**
	 * Methods under test:
	 * 
	 *		@class	ServerInterface
	 *			@method void sendClientConnected(String toNickname, String connectedNickname)
	 *			@method void sendClientDisconnected(String toNickname, String disconnectedNickname)
	 *			@method void sendError(String toNickname, String error) 
	 *
	 *
	 * @throws InterruptedException 
	 * @throws IOException 
	 * @throws UnknownHostException 
	 */
	@Test
	public void test_sendConnectionMessages_and_sendError() throws UnknownHostException, IOException, InterruptedException{
		System.out.println("\n\n*** BEGIN TEST ***");
		System.out.println("**@class=NetworkCommunicationTest @method=sendConnectionMessages()\n**");


		//Preparing Test.. Giving Time for resources to be free
		Thread.sleep(100);

		//Open connection
		serverInterface.start();

		clientInterface1.connect(HOST, CLIENT_1_NICKNAME);
		clientInterface2.connect(HOST, CLIENT_2_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//Server informs both clients who who connects

		serverInterface.sendClientConnected(CLIENT_1_NICKNAME, CLIENT_2_NICKNAME);
		serverInterface.sendClientConnected(CLIENT_2_NICKNAME, CLIENT_1_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//clients receives connection info of other client relayed by server

		Assert.assertTrue(clientInterface1.hasNextMessage());
		Assert.assertTrue(clientInterface2.hasNextMessage());
		ClientSideMessage msgAtClient1 = clientInterface1.getNextMessage();
		ClientSideMessage msgAtClient2 = clientInterface2.getNextMessage();

		Assert.assertEquals("type of message should be " + ClientSideMessage.Type.CONNECTED.toString(),ClientSideMessage.Type.CONNECTED.toString(),msgAtClient1.getType().toString());
		Assert.assertEquals(CLIENT_2_NICKNAME + " should be connected",CLIENT_2_NICKNAME, msgAtClient1.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER, msgAtClient1.getSendersNickname().toString());	


		Assert.assertEquals("type of message should be " + ClientSideMessage.Type.CONNECTED.toString(),ClientSideMessage.Type.CONNECTED.toString(),msgAtClient2.getType().toString());
		Assert.assertEquals(CLIENT_1_NICKNAME + " should be connected",CLIENT_1_NICKNAME, msgAtClient2.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER, msgAtClient2.getSendersNickname().toString());	


		//Server informs clients who is disconnected

		serverInterface.sendClientDisconnected(CLIENT_1_NICKNAME, CLIENT_2_NICKNAME);
		serverInterface.sendClientDisconnected(CLIENT_2_NICKNAME, CLIENT_1_NICKNAME);

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		//clients receives connection info of other client relayed by server

		Assert.assertTrue(clientInterface1.hasNextMessage());
		Assert.assertTrue(clientInterface2.hasNextMessage());
		ClientSideMessage msgAtClient3 = clientInterface1.getNextMessage();
		ClientSideMessage msgAtClient4 = clientInterface2.getNextMessage();

		Assert.assertEquals("type of message should be " + ClientSideMessage.Type.DISCONNECTED.toString(),ClientSideMessage.Type.DISCONNECTED.toString(),msgAtClient3.getType().toString());
		Assert.assertEquals(CLIENT_2_NICKNAME + " should be disconnected",CLIENT_2_NICKNAME, msgAtClient3.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER, msgAtClient3.getSendersNickname().toString());	


		Assert.assertEquals("type of message should be " + ClientSideMessage.Type.DISCONNECTED.toString(),ClientSideMessage.Type.DISCONNECTED.toString(),msgAtClient4.getType().toString());
		Assert.assertEquals(CLIENT_1_NICKNAME + " should be disconnected",CLIENT_1_NICKNAME, msgAtClient4.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER, msgAtClient4.getSendersNickname().toString());	


		serverInterface.sendError(CLIENT_1_NICKNAME , "error 666");
		//server sends error msg to client 1

		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		Assert.assertTrue(clientInterface1.hasNextMessage());
		ClientSideMessage msgAtClient_error = clientInterface1.getNextMessage();


		Assert.assertEquals("type of message should be " + ClientSideMessage.Type.ERROR.toString(),ClientSideMessage.Type.ERROR.toString(), msgAtClient_error.getType().toString());
		Assert.assertEquals("error description should be " + "error 666","error 666",  msgAtClient_error.getPayload().toString());
		Assert.assertEquals("msg sent by  " + ClientSideMessage.SERVER,ClientSideMessage.SERVER,  msgAtClient_error.getSendersNickname().toString());	



		//Close connection
		clientInterface1.disconnect();
		clientInterface2.disconnect();
		//(this) process needs to sleep to give time for processes(client and server)to exchange messages
		Thread.sleep(10);

		serverInterface.shutdown();

		//Preparing Test.. Giving Time for other processes to finish
		Thread.sleep(100);
		System.out.println("\n**@class=NetworkCommunicationTest @method=sendConnectionMessages()**");
		System.out.println("*** END TEST ***\n");

	}

}
